#include "StarUtil.h"
using namespace BWAPI;
using namespace std;


static vector<string> playerTypes, enemyTypes;
static map<string, vector<Unit*>*> playerGroups, enemyGroups;


bool StarUtil::isMelee(UnitType& unit){
	if(unit.isFlyer()){
		return false;
	} else if(unit.groundWeapon().maxRange() > 50){
		return false;
	}
	return true;
}


bool StarUtil::unitCanAttack(const string& attacker, const string& target)
 {
	return getWeapon(attacker, target).getName() != "None";
}


vector<string>& StarUtil::getPlayerTypes(){
	static bool isDetected = false;

	if(!isDetected){
		getUnitTypes(Broodwar->self(), playerTypes);
		isDetected = true;
	}
	return playerTypes;
}

vector<string>& StarUtil::getEnemyTypes(){
	static bool isDetected = false;

	if(!isDetected){
		getUnitTypes(getEnemy(), enemyTypes);
		isDetected = true;
	}

	return enemyTypes;
}

map<string, vector<Unit*>*>& StarUtil::getPlayerGroups(){
	static bool isDetected = false;

	if(!isDetected){
		getUnitGroups(Broodwar->self(), playerGroups);
		isDetected = true;
	}
	
	return playerGroups;
}

map<string, vector<Unit*>*>& StarUtil::getEnemyGroups(){
	static bool isDetected = false;

	if(!isDetected){
		getUnitGroups(getEnemy(), enemyGroups);
		isDetected = true;
	}
	//Broodwar->printf("Enemy groups initialized to %d vectors",
	//enemyGroups.size());
	return enemyGroups;
}

void StarUtil::getUnitGroups(Player* player, 
							 map<string, vector<Unit*>*>& unitGroups)
{
	vector<string> playerTypes;
	if(player->getID() == Broodwar->self()->getID()){
		playerTypes = getPlayerTypes();	
	}else{
		playerTypes = getEnemyTypes();
	}

	set<Unit*> playerUnits = player->getUnits();
	
	for(vector<string>::iterator it = playerTypes.begin(); 
		it != playerTypes.end(); it++)
	{
		unitGroups[*it] = new vector<Unit*>;
	}
	for(set<Unit*>::iterator it = playerUnits.begin(); 
		it != playerUnits.end(); it++)
	{
		string unitName = (*it)->getType().getName();
		if(unitGroups[unitName] != NULL)
			unitGroups[unitName]->push_back(*it);
	}
}
 void StarUtil::getUnitTypes(Player * player, vector<string> &unitTypes)
{
	std::set<Unit*> units = player->getUnits();

	for(std::set<Unit*>::iterator it=units.begin();it!=units.end();it++)
	{
		bool add = true;
		for(vector<string>::iterator type = unitTypes.begin();
			type != unitTypes.end(); type++)
			if((*it)->getType().getName() == (*type))
				add = false;
		if(add){
			string name =(*it)->getType().getName();
			unitTypes.push_back(name);
		}
	}
}

vector<string> StarUtil::getSelfUnitTypes()
{
	vector<string> units;
	getUnitTypes(Broodwar->self(),units);
	return units;
}

Player* StarUtil::getEnemy()
{
	set<Player*> players = Broodwar->getPlayers();
	for(set<Player*>::iterator it = players.begin(); it != players.end(); it++)
		if(Broodwar->self()->isEnemy(*it))
			return *it;
	return NULL;
}

void StarUtil::deleteValues()
{
	for(map<string, vector<Unit*>*>::iterator it = playerGroups.begin();
		it != playerGroups.end(); it++)
	{
		delete (*it).second;
	}

	for(map<string, vector<Unit*>*>::iterator it = enemyGroups.begin();
		it != enemyGroups.end(); it++)
	{
		delete (*it).second;
	}

}

// Returns the type of weapon it uses.  If it cannot attack the target
// returns WeaponTypes::None
WeaponType StarUtil::getWeapon(const string& attacker,const  string& target)
{
	UnitType _attacker = UnitTypes::getUnitType(attacker);
	UnitType _target = UnitTypes::getUnitType(target);
	WeaponType gWeapon = _attacker.groundWeapon();
	WeaponType aWeapon = _attacker.airWeapon();
	if(!_target.isFlyer()) // is a ground unit
	{
		if(gWeapon != WeaponTypes::None)
			return gWeapon;
		else if(aWeapon.targetsGround())
			return aWeapon;
	}
	else // is a flying unit
	{
		if(aWeapon != WeaponTypes::None)
			return aWeapon;
		else if(gWeapon.targetsAir())
			return gWeapon;
	}
	return WeaponTypes::None;

}